// 此源代码的使用受BSD样式
// 许可证的约束，该许可证可以在许可证文件中找到。

package noder

import (
	"fmt"
	"os"

	"cmd/compile/internal/base"
	"cmd/compile/internal/dwarfgen"
	"cmd/compile/internal/ir"
	"cmd/compile/internal/syntax"
	"cmd/compile/internal/typecheck"
	"cmd/compile/internal/types"
	"cmd/compile/internal/types2"
	"cmd/internal/src"
)

// checkFiles在给定的
// 解析的源文件上配置并运行types2检查器，然后返回结果。
func checkFiles(noders []*noder) (posMap, *types2.Package, *types2.Info) {
	if base.SyntaxErrors() != 0 {
		base.ErrorExit()
	}

	// 设置和语法错误报告
	var m posMap
	files := make([]*syntax.File, len(noders))
	for i, p := range noders {
		m.join(&p.posMap)
		files[i] = p.file
	}

	// 类型检查
	ctxt := types2.NewContext()
	importer := gcimports{
		ctxt:     ctxt,
		packages: map[string]*types2.Package{"unsafe": types2.Unsafe},
	}
	conf := types2.Config{
		Context:               ctxt,
		GoVersion:             base.Flag.Lang,
		IgnoreLabels:          true, // 语法分析器已通过语法检查。CheckBranchs模式
		CompilerErrorMessages: true, // 使用与现有编译器错误匹配的错误字符串
		Error: func(err error) {
			terr := err.(types2.Error)
			base.ErrorfAt(m.makeXPos(terr.Pos), "%s", terr.Msg)
		},
		Importer: &importer,
		Sizes:    &gcSizes{},
	}
	info := &types2.Info{
		Types:      make(map[syntax.Expr]types2.TypeAndValue),
		Defs:       make(map[*syntax.Name]types2.Object),
		Uses:       make(map[*syntax.Name]types2.Object),
		Selections: make(map[*syntax.SelectorExpr]*types2.Selection),
		Implicits:  make(map[syntax.Node]types2.Object),
		Scopes:     make(map[syntax.Node]*types2.Scope),
		Instances:  make(map[*syntax.Name]types2.Instance),
		// 根据需要展开
	}

	pkg, err := conf.Check(base.Ctxt.Pkgpath, files, info)

	base.ExitIfErrors()
	if err != nil {
		base.FatalfAt(src.NoXPos, "conf.Check error: %v", err)
	}

	return m, pkg, info
}

// check2 type使用types2检查Go包，然后使用结果生成IR 
// 。
func check2(noders []*noder) {
	m, pkg, info := checkFiles(noders)

	if base.Flag.G < 2 {
		os.Exit(0)
	}

	g := irgen{
		target: typecheck.Target,
		self:   pkg,
		info:   info,
		posMap: m,
		objs:   make(map[types2.Object]*ir.Name),
		typs:   make(map[types2.Type]*types.Type),
	}
	g.generate(noders)

	if base.Flag.G < 3 {
		os.Exit(0)
	}
}

// 有关字典中子字典项的信息
type subDictInfo struct {
	// 需要字典的调用或XDOT节点。
	callNode ir.Node
	// 已保存CallExpr。X节点（*ir.SelectorExpr或*InstExpr节点）用于通用
	// 方法或函数调用，因为当通用
	// 方法/函数调用转换为对实例化形状
	// 函数的调用时，此节点将被删除。其他类型的呼叫或XDOT为零。
	savedXNode ir.Node
}

// dictInfo是一种字典格式，用于实例化具有特定形状的泛型函数。ShapeParam、derivedTypes、SubictCalls和ItabConv按顺序描述实际的字典条目，其余字段是编译过程中进行字典处理所需的其他信息。
type dictInfo struct {
	// 替换类型参数的类型，即形状类型。
	shapeParams []*types.Type
	// 从实例化中使用的那些类型参数派生的所有类型。
	derivedTypes []*types.Type
	// 实例化中需要子字典的节点。包括
	// 方法和函数调用（OCALL）、函数值（OFUNINST）、方法
	// 值/表达式（OXDOT）。
	subDictCalls []subDictInfo
	// 实例化中的节点，它们是从typeparam/派生的
	// 类型到特定接口的转换。
	itabConvs []ir.Node

	// 从替换类型参数的每个形状类型到其
	// 类型绑定（如果已参数化，也将替换为形状）的映射
	shapeToBound map[*types.Type]*types.Type

	// 对于非空接口上的类型开关，从
	// hashape type的OTYPE条目到我们正在切换的接口类型的映射。
	type2switchType map[ir.Node]*types.Type

	startSubDict  int // 子词典的dict条目开始
	startItabConv int // itab转换的dict条目开始
	dictLen       int // 词典中的条目总数
}

// instInfo是关于函数的形状实例化收集的信息。
type instInfo struct {
	fun       *ir.Func // 实例化函数（带body）
	dictParam *ir.Name // fun中的节点引用字典参数

	dictInfo *dictInfo
}

type irgen struct {
	target *ir.Package
	self   *types2.Package
	info   *types2.Info

	posMap
	objs   map[types2.Object]*ir.Name
	typs   map[types2.Type]*types.Type
	marker dwarfgen.ScopeMarker

	// laterFuncs记录处理所有声明
	// 后需要运行的任务。
	laterFuncs []func()
	// haveEmbed表示当前节点是否属于
	// 导入“嵌入”包的文件。
	haveEmbed bool

	// exprStmtOK表示生成表达式或
	// 语句是否安全。
	exprStmtOK bool

	// 我们需要通过g.fillin方法完成的类型。
	typesToFinalize []*typeDelayInfo

	// 编译顶级泛型函数或方法时为True。用于
	// 避免向目标添加泛型函数/方法的闭包。Decls 
	// 列表。
	topFuncIsGeneric bool

	// 类型/函数/方法声明期间的上下文，用于
	// 唯一命名类型参数。我们需要类型参数的唯一名称，因此我们可以确保它们在类型2到类型1的翻译
	// 和类型1导入之间正确匹配。
	curDecl string
}

// genInst拥有创建所需实例化和修改
// 函数以使用实例化的信息。
type genInst struct {
	dnum int // 用于生成唯一的字典变量

	// 从所有实例化的名称映射到有关
	// 实例化的信息。
	instInfoMap map[*types.Sym]*instInfo

	// 我们需要通过写出任何itabconv 
	// 条目来完成字典符号。
	dictSymsToFinalize []*delayInfo

	// 在这一轮BuildInstances（）中创建了新的实例化。
	newInsts []ir.Node
}

func (g *irgen) later(fn func()) {
	g.laterFuncs = append(g.laterFuncs, fn)
}

type delayInfo struct {
	gf     *ir.Name
	targs  []*types.Type
	sym    *types.Sym
	off    int
	isMeth bool
}

type typeDelayInfo struct {
	typ  *types2.Named
	ntyp *types.Type
}

func (g *irgen) generate(noders []*noder) {
	types.LocalPkg.Name = g.self.Name()
	types.LocalPkg.Height = g.self.Height()
	typecheck.TypecheckAllowed = true

	// 防止大小计算，直到我们为所有包块定义的类型设置基础类型
	// 为止。此时，types2已经处理了名称解析和类型检查。我们只需要将其对象和类型
	types.DeferCheckSize()

	// 表示映射到
	// 编译器其余部分当前使用的表示。这种情况会在几次过程中发生。

	// 1。处理所有进口申报。我们使用编译器自己的
	// 导入器，而不是types2的gcimporter派生的导入器，
	// 来正确处理扩展和内联函数体。
	// 
	// 同样，我们需要在单独的过程中完成这项工作，因为映射是按需实例化的。如果我们将导入
	// 声明与其他声明交错处理，很可能我们最终会发现
	// 想要从另一个源文件映射一个对象/类型，但不是
	// 还拥有它所依赖的导入数据。
	declLists := make([][]syntax.Decl, len(noders))
Outer:
	for i, p := range noders {
		g.pragmaFlags(p.file.Pragma, ir.GoBuildPragma)
		for j, decl := range p.file.DeclList {
			switch decl := decl.(type) {
			case *syntax.ImportDecl:
				g.importDecl(p, decl)
			default:
				declLists[i] = p.file.DeclList[j:]
				continue Outer // 不再进口
			}
		}
	}

	// 2。处理所有包块类型声明。与导入一样，我们需要确保在映射任何使用它们的表达式之前，所有类型都已正确实例化。尤其是
	// 我们需要确保已经知道类型pragmas（参见irgen.typeDecl中的注释
	// ）。
	// 
	// 我们或许可以像noder那样推迟处理包块
	// 变量初始值设定项和函数体，但是
	// 特殊的case just package block类型声明最小化了
	// 处理包块和函数范围
	// 声明之间的差异。
	for _, declList := range declLists {
		for _, decl := range declList {
			switch decl := decl.(type) {
			case *syntax.TypeDecl:
				g.typeDecl((*ir.Nodes)(&g.target.Decls), decl)
			}
		}
	}
	types.ResumeCheckSize()

	// 3。处理所有剩余的声明。
	for i, declList := range declLists {
		old := g.haveEmbed
		g.haveEmbed = noders[i].importedEmbed
		g.decls((*ir.Nodes)(&g.target.Decls), declList)
		g.haveEmbed = old
	}
	g.exprStmtOK = true

	// 4。运行任何“以后”任务。避免使用“range”，这样任务就可以对更多任务进行递归排队。（但目前尚未使用。）
	for len(g.laterFuncs) > 0 {
		fn := g.laterFuncs[0]
		g.laterFuncs = g.laterFuncs[1:]
		fn()
	}

	if base.Flag.W > 1 {
		for _, n := range g.target.Decls {
			s := fmt.Sprintf("\nafter noder2 %v", n)
			ir.Dump(s, n)
		}
	}

	for _, p := range noders {
		// 进程链接名和cgo pragmas。
		p.processPragmas()

		// 仔细检查任何类型检查不一致。一旦我们对IR生成结果有信心，这可以被
		// 删除。
		syntax.Crawl(p.file, func(n syntax.Node) bool {
			g.validate(n)
			return false
		})
	}

	if base.Flag.Complete {
		for _, n := range g.target.Decls {
			if fn, ok := n.(*ir.Func); ok {
				if fn.Body == nil && fn.Nname.Sym().Linkname == "" {
					base.ErrorfAt(fn.Pos(), "missing function body")
				}
			}
		}
	}

	// 检查节点2遇到类型2 
	// 未检查的类型错误的异常情况（例如notinheap不兼容）。
	base.ExitIfErrors()

	typecheck.DeclareUniverse()

	// 创建任何需要的泛型函数实例化，并转换
	// 现有和新函数以使用这些实例化。
	BuildInstantiations()

	// 从g.target中删除所有通用函数。Decl，因为它们被
	// 用于制版，但不编译。通用函数已经
	// 已根据需要标记为导出。
	j := 0
	for i, decl := range g.target.Decls {
		if decl.Op() != ir.ODCLFUNC || !decl.Type().HasTParam() {
			g.target.Decls[j] = g.target.Decls[i]
			j++
		}
	}
	g.target.Decls = g.target.Decls[:j]

	base.Assertf(len(g.laterFuncs) == 0, "still have %d later funcs", len(g.laterFuncs))
}

func (g *irgen) unhandled(what string, p poser) {
	base.FatalfAt(g.pos(p), "unhandled %s: %T", what, p)
	panic("unreachable")
}

// 如果我们应该延迟所有转换，则delayTransform返回true，因为我们正在为通用函数/方法创建节点。
func (g *irgen) delayTransform() bool {
	return g.topFuncIsGeneric
}
